package cc.rengu.igas.momp.core.realize.impl;

import cc.rengu.igas.momp.common.constant.CommonConstant;
import cc.rengu.igas.momp.common.enums.RespCodeEnum;
import cc.rengu.igas.momp.core.model.TransactionResource;
import cc.rengu.igas.momp.core.realize.TransactionResourceService;
import cc.rengu.oltp.service.common.entity.SysParam;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.DateUtil;
import cc.rengu.oltp.utility.util.RedisUtil;
import cc.rengu.utility.base.StringUtil;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;

import java.util.ArrayList;
import java.util.List;

/**
 * 交易资源池接口实现
 *
 * @author Jinan Liu
 * @version 1.0.0
 * @date 2020/7/24 14:56
 */

public class TransactionResourceServiceImpl implements TransactionResourceService {

    /**
     * 日志
     */
    private RgLogger rglog = RgLog.getLogger(this.getClass().getName());
    /**
     * 资源池超时时间参数PARAM_TYPE
     */
    private static final String MOMP_RESOURCE_EXPIRE_PARAM = "MOMP_RESOURCE_EXPIRE_PARAM";
    /**
     * 资源池缓存时间参数PARAM_KEY
     */
    private static final String MOMP_RESOURCE_EXPIRE_TIME = "MOMP_RESOURCE_EXPIRE_TIME";
    /**
     * 资源池缓存默认有效时长
     */
    private static final long MOMP_RESOURCE_EXPIRE_SECONDS = 300L;
    /**
     * KEY值常量头
     */
    private static final String HEADER = "mompTxnResource:list:";
    /**
     * 资源池最大交易数量PARAM_KEY
     */
    private static final String MOMP_RESOURCE_MAX_LIMIT = "MOMP_RESOURCE_MAX_LIMIT";
    /**
     * 资源池默认最大交易数量
     */
    private static final int MOMP_RESOURCE_MAX_VALUE = 50;

    /**
     * 向交易资源池PUSH单条记录接口实现
     *
     * @param transactionResource 营销模块交易资源实体类
     * @throws Exception 异常信息
     * @author Jinan Liu
     * @date 2020/7/24 16:39
     */
    @Override
    public void pushSingleRecordToResourcePool(TransactionResource transactionResource) throws Exception {

        try {

            /* 信息校验 */
            if (null == transactionResource || StringUtil.isNullorEmpty(transactionResource.getInstId()) || StringUtil.isNullorEmpty(transactionResource.getTxnNum())) {
                rglog.error("交易资源池参数为空!");
                throw new BizException(RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode(), RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespDesc());
            }

            /* 清理超时资源 */
            List<TransactionResource> transactionResourceList = new ArrayList<>();
            transactionResourceList.add(transactionResource);
            purgeTimeoutTxnFromResourcePool(transactionResourceList);

            /* 组装KEY */
            String redisKey = HEADER + transactionResource.getInstId().trim() + transactionResource.getTxnNum().trim();

            SysParamService sysParamService = new SysParamServiceImpl();

            /* 获取缓存时间 */
            SysParam sysParam = sysParamService.getSysParamInfo(transactionResource.getInstId().trim(), MOMP_RESOURCE_EXPIRE_PARAM, MOMP_RESOURCE_EXPIRE_TIME);
            long cacheMonitorTime = (null == sysParam) ? MOMP_RESOURCE_EXPIRE_SECONDS : Long.parseLong(sysParam.getParamValue().trim());

            /* 获取资源池资源最大数量 */
            sysParam = sysParamService.getSysParamInfo(transactionResource.getInstId().trim(), MOMP_RESOURCE_EXPIRE_PARAM, MOMP_RESOURCE_MAX_LIMIT);
            int maxNum = (null == sysParam) ? MOMP_RESOURCE_MAX_VALUE : Integer.parseInt(sysParam.getParamValue().trim());

            /* 根据当前资源池数量判断是否可以操作资源池 */
            int currentNum = checkSingleTxnStatusFromResourcePool(transactionResource);
            if (CommonConstant.ZERO > currentNum) {
                rglog.error("当前资源池状态异常!");
                throw new BizException(RespCodeEnum.ABNORMAL_REDIS_RESOURCE_POOL_ERROR.getRespCode(), RespCodeEnum.ABNORMAL_REDIS_RESOURCE_POOL_ERROR.getRespDesc());
            } else if (maxNum <= currentNum) {
                rglog.error("当前资源池已满,不可继续操作!");
                throw new BizException(RespCodeEnum.PUSH_REDIS_RESOURCE_ERROR.getRespCode(), RespCodeEnum.PUSH_REDIS_RESOURCE_ERROR.getRespDesc());
            } else {
                /* 将本交易记录压入资源栈中 */
                long currentTimeStamp = DateUtil.getCurrentTimeStamp();
                RedisUtil.onceRightPush(redisKey, (currentTimeStamp + cacheMonitorTime) + "|" + transactionResource.getTxnNum().trim());
                rglog.info("<{}>资源成功压入资源池", redisKey);
            }

        } catch (Exception e) {
            rglog.error("交易资源压入资源池失败! <{}>", StringUtil.ExceptionToString(e));
            throw e;
        }
    }

    /**
     * 从交易资源池POP单条记录接口实现
     *
     * @param transactionResource 营销模块交易资源实体类
     * @throws Exception 异常信息
     * @author Jinan Liu
     * @date 2020/7/24 16:39
     */
    @Override
    public void popSingleRecordFromResourcePool(TransactionResource transactionResource) throws Exception {

        try {

            /* 信息校验 */
            if (null == transactionResource || StringUtil.isNullorEmpty(transactionResource.getInstId()) || StringUtil.isNullorEmpty(transactionResource.getTxnNum())) {
                rglog.error("交易资源池参数为空!");
                throw new BizException(RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode(), RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespDesc());
            }

            /* 组装KEY */
            String redisKey = HEADER + transactionResource.getInstId().trim() + transactionResource.getTxnNum().trim();

            /* 将本交易记录弹出资源栈 */
            RedisUtil.onceLeftPop(redisKey);

            rglog.info("<{}>资源成功弹出资源池", redisKey);

        } catch (Exception e) {
            rglog.error("交易资源弹出资源池失败! <{}>", StringUtil.ExceptionToString(e));
            throw e;
        }
    }

    /**
     * 查询交易资源池状态接口实现
     * 查询成功返回值大于等于0
     * 查询失败抛异常
     *
     * @param transactionResource 营销模块交易资源实体类
     * @return int 资源数量
     * @throws Exception 异常信息
     * @author Jinan Liu
     * @date 2020/7/25 11:07
     */
    @Override
    public int checkSingleTxnStatusFromResourcePool(TransactionResource transactionResource) throws Exception {

        try {

            /* 信息校验 */
            if (null == transactionResource || StringUtil.isNullorEmpty(transactionResource.getInstId()) || StringUtil.isNullorEmpty(transactionResource.getTxnNum())) {
                rglog.error("交易资源池参数为空!");
                throw new BizException(RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode(), RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespDesc());
            }

            /* 组装KEY */
            String redisKey = HEADER + transactionResource.getInstId().trim() + transactionResource.getTxnNum().trim();

            /* 删除KEY对应的已经过期的交易监控信息 */
            List<String> messageList = RedisUtil.getListRange(redisKey, 0, -1);
            if (null == messageList || messageList.isEmpty()) {
                return CommonConstant.ZERO;
            } else {
                rglog.info("当前redis资源池资源数量=<{}>", messageList.size());
                return messageList.size();
            }

        } catch (Exception e) {
            rglog.error("检查资源池资源失败! <{}>", StringUtil.ExceptionToString(e));
            throw e;
        }
    }

    /**
     * 清理已超时交易资源接口实现
     *
     * @param transactionResourceList 营销模块交易资源实体类
     * @throws Exception 异常信息
     * @author Jinan Liu
     * @date 2020/7/25 11:07
     */
    @Override
    public void purgeTimeoutTxnFromResourcePool(List<TransactionResource> transactionResourceList) throws Exception {

        try {

            rglog.info("开始清理交易资源池已超时资源!");

            /* 信息校验 */
            if (null == transactionResourceList || transactionResourceList.isEmpty()) {
                rglog.error("交易资源池参数为空!");
                throw new BizException(RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode(), RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespDesc());
            }

            /* 清理资源池中已超时所有交易信息 */
            for (TransactionResource transactionResource : transactionResourceList) {

                /* 组装KEY */
                String redisKey = HEADER + transactionResource.getInstId().trim() + transactionResource.getTxnNum().trim();

                /* 删除KEY对应的已经过期的交易监控信息 */
                List<String> messageList = RedisUtil.getListRange(redisKey, 0, -1);
                if (null != messageList && !messageList.isEmpty()) {
                    int deleteNum = 0;
                    for (String message : messageList) {
                        long timeout = Long.parseLong(message.substring(0, message.indexOf('|')));
                        if (timeout > System.currentTimeMillis()) {
                            break;
                        } else {
                            rglog.info("当前交易监控信息已超过最大缓存时间!");
                            deleteNum++;
                        }
                    }
                    String returnCode = RedisUtil.onceListTrim(redisKey, deleteNum, -1);
                    rglog.info("删除已经过期的交易监控信息,returnCode:<{}>", returnCode);
                }
            }

            rglog.info("交易资源池清理完毕!");

        } catch (Exception e) {
            rglog.error("交易资源池清理失败! <{}>", StringUtil.ExceptionToString(e));
            throw e;
        }
    }
}
